package com.yn.sample.util;

import com.yn.sample.domain.ConstantPoolItem;
import com.yn.sample.domain.ConstantPoolItemTypeEnum;
import com.yn.sample.domain.MethodCodeStackSizeAndLocalVariablesTableSize;
import com.yn.sample.domain.MethodInstructionVO;
import org.springframework.util.StringUtils;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ParseEngineHelper {
    private static Pattern CONSTANT_POOL_PATTERN =
            Pattern.compile("\\s+(#\\d{1,3})\\s+=\\s+(\\w+)\\s+(\\S+)(.*)?");

    private static Pattern METHOD_SIGNATURE_PATTERN =
            Pattern.compile("\\s+(\\w+)\\s+(\\S+);");

    private static Pattern STACK_SIZE_AND_LOCAL_VARIABLES_SIZE_PATTERN =
            Pattern.compile("\\s+stack=(\\d+),\\s+locals=(\\d+),\\s+args_size=(\\d+).*");

    /**
     * 示例如下：
     * com/yn/sample/CheckAndSet.f:I
     * 要提取其中的com/yn/sample/CheckAndSet、f
     */
    private static Pattern CONSTANT_POOL_COMMENT_PATTERN =
            Pattern.compile("\\s{0,}(\\S+)\\.(\\S+):(\\S+).*$");


    public static ConstantPoolItem parseConstantPoolItem(String line) {
        Matcher matcher = CONSTANT_POOL_PATTERN.matcher(line);
        if (matcher.find()) {
            ConstantPoolItem item = new ConstantPoolItem();
            item.setId(matcher.group(1));
            String typeEnumStr = matcher.group(2);
            ConstantPoolItemTypeEnum typeEnum = ConstantPoolItemTypeEnum.valueOf(typeEnumStr);
            item.setConstantPoolItemTypeEnum(typeEnum);

            item.setValue(matcher.group(3));
            String group = matcher.group(4);
            if (!StringUtils.isEmpty(group)) {
                String[] split = group.split("\\s+");
                item.setComment(split[split.length - 1]);
            }

            return item;
        }
        return null;
    }


    public static String getMethodSignature(String currentLine) {
        Matcher matcher = METHOD_SIGNATURE_PATTERN.matcher(currentLine);
        if (matcher.find()) {
            return matcher.group(2);
        }
        return null;
    }

    public static MethodInstructionVO parseMethodByteCodeInstruction(String currentLine) {
        String[] array = currentLine.split("\\s+");

        MethodInstructionVO instructionVO = new MethodInstructionVO();
        if (array.length == 3) {
            /**
             * 如下格式的时候，只有指令的序列号和操作码，没有操作数
             * 0: iload_1
             */
            instructionVO.setSequenceNumber(array[1].substring(0,array[1].length() - 1));
            instructionVO.setOpcode(array[2]);
        } else if (array.length == 4) {
            /**
             * 如下格式的时候，有指令的序列号和操作码，有操作数
             * 1: iflt          12
             * 这条指令表示的是，如果栈顶元素小于0，则跳转到sequenceNumber为12的字节码指令
             */
            instructionVO.setSequenceNumber(array[1].substring(0,array[1].length() - 1));
            instructionVO.setOpcode(array[2]);
            instructionVO.setOperand(array[3]);
        } else if (array.length == 7) {
            /**
             * 如下格式的时候，只有指令的序列号和操作码，没有操作数
             * 0: iload_1
             */
            instructionVO.setSequenceNumber(array[1].substring(0,array[1].length() - 1));
            instructionVO.setOpcode(array[2]);
            instructionVO.setOperand(array[3]);
            instructionVO.setComment(array[5] + " " + array[6]);
        }

        return instructionVO;
    }

    public static void main(String[] args) {
        getClassAndFieldName("        com/yn/sample/CheckAndSet.f:I        ");

        parseMethodByteCodeInstruction("         0: iload_1       ");
        parseMethodByteCodeInstruction("         1: iflt          12");
        parseMethodByteCodeInstruction("         6: putfield      #2                  // Field f:I");
    }

    public static MethodCodeStackSizeAndLocalVariablesTableSize getMethodStackSizeAndLocalVariablesSize(String currentLine) {
        Matcher matcher = STACK_SIZE_AND_LOCAL_VARIABLES_SIZE_PATTERN.matcher(currentLine);
        if (matcher.find()) {
            String stackSize = matcher.group(1);
            String localVariablesTableSize = matcher.group(2);
            String argsSize = matcher.group(3);

            MethodCodeStackSizeAndLocalVariablesTableSize vo = new MethodCodeStackSizeAndLocalVariablesTableSize();
            vo.setStackSize(Integer.valueOf(stackSize));
            vo.setLocalVariablesSize(Integer.valueOf(localVariablesTableSize));
            vo.setArgumentsSize(Integer.valueOf(argsSize));


            return vo;
        }

        return null;
    }

    public static String[] getClassAndFieldName(String comment) {
        Matcher matcher = CONSTANT_POOL_COMMENT_PATTERN.matcher(comment);
        if (matcher.find()) {
            String className = matcher.group(1).replaceAll("/", ".");
            String fieldName = matcher.group(2);
            return new String[]{className,fieldName};
        }
        return null;
    }


}
